package tako.wisers.com.android.upload;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Query;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;


@SuppressWarnings("unchecked")
public class Uploader {
	private PersistenceManagerFactory pmf;

	private HttpServletRequest req;

	private HttpServletResponse resp;

	private String charset;

	public static int SELECT = 1;

	public static int DELETE = 2;
	
	private final SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

	public Uploader(PersistenceManagerFactory pmf) {
		this.pmf = pmf;
	}

	public void uploaderExecute(HttpServletRequest req, HttpServletResponse resp)
			throws UnsupportedEncodingException {
		this.req = req;
		this.resp = resp;

		long timeStart = System.currentTimeMillis();
		String password = req.getHeader("password");
		Long version = new Long(0);
		if (req.getHeader("version") != null
				&& !req.getHeader("version").equals("")) {
			version = Long.parseLong(req.getHeader("version"));
		}
		if (!password.equals("12345")) {
			resp.setStatus(400);
			return;
		}
		PersistenceManager pm = null;
		try {
			pm = pmf.getPersistenceManager();
			String command = req.getHeader("command");
			charset = req.getHeader("charset");
			String cmds[] = null;
			if (command != null) {
				cmds = command.split(" ");
				if (cmds.length < 2) {
					resp.setHeader("error", "command < 2");
					resp.setStatus(500);
				}
			}
			if (cmds[0].equalsIgnoreCase("delete")) {
				delete(version, pm, command);
			} else if (cmds[0].equalsIgnoreCase("insert")) {
				insert(timeStart, version, pm);
			} else if (cmds[0].equalsIgnoreCase("select")) {
				select(timeStart, version, pm);
			} else if (cmds[0].equalsIgnoreCase("update")) {
				update(timeStart, version, pm);
			}
		} catch (Exception e) {
			resp.setIntHeader("update", 0);
			resp.setIntHeader("select", 0);
			resp.setIntHeader("insert", 0);
			resp.setHeader("error", URLEncoder.encode(e.getLocalizedMessage(),
					charset));
		} finally {
			pm.close();
		}
	}

	private int update(long timeStart, Long version, PersistenceManager pm)
			throws UnsupportedEncodingException {
		int index_upd = 0;
		try {
			List<?> entityList = new RequestDecompose().getFileEntity(req,
					charset, version);
			Iterator itrEntity = entityList.iterator();
			while (itrEntity.hasNext()) {
				Object obj = itrEntity.next();
				if (!(obj instanceof EntityBase)) {
					throw new IllegalArgumentException(
							"entity have not implements JdoInterface interface!");
				}
				EntityBase jdo = (EntityBase) obj;
				jdo.update(pm);
				long timeEnd = System.currentTimeMillis();
				index_upd++;
				if (timeEnd - timeStart > 25000) {
					break;

				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			resp.setHeader("error", URLEncoder.encode(e.getLocalizedMessage(),
					charset));
		}
		resp.setIntHeader("update", index_upd);
		return index_upd;
	}

	private int select(long timeStart, Long version, PersistenceManager pm)
			throws UnsupportedEncodingException {
		int index_sel = 0;
		try {
			String cmd = req.getHeader("command");
			Boolean isStr = new Boolean(req.getHeader("getType"));

			List buffList = new ArrayList();

			List result = parserCmd(cmd, pm, SELECT);
			Iterator tempItr = result.iterator();

			while (tempItr.hasNext()) {
				if (isStr) {
					buffList.add(tempItr.next().toString());
				} else {
					buffList.add(tempItr.next());
				}
				long timeEnd = System.currentTimeMillis();
				index_sel++;
				if (timeEnd - timeStart > 25000) {
					break;

				}
			}

			ServletOutputStream fout = resp.getOutputStream();
			IOUtils.write(object2Bytes(buffList), fout);
		} catch (Exception e) {
			e.printStackTrace();
			resp.setHeader("error", URLEncoder.encode(e.getLocalizedMessage(),
					charset));
		}
		resp.setIntHeader("size", index_sel);
		return index_sel;
	}

	private int insert(long timeStart, Long version, PersistenceManager pm)
			throws UnsupportedEncodingException {
		int index_ins = 0;
		Integer groupId = new Integer(req.getHeader("groupId"));
		try {
			List<?> entityList = new RequestDecompose().getFileEntity(req,
					charset, version);
			Iterator itrEntity = entityList.iterator();
			while (itrEntity.hasNext()) {
				Object obj = itrEntity.next();
				if (obj instanceof String) {
					String className = req.getHeader("className");
					Method m = Class.forName(className).getMethod("insert",
							PersistenceManager.class, String.class,
							Integer.class);
					m.invoke(Class.forName(className).newInstance(), pm,
							((String) obj), groupId);
				} else {
					pm.makePersistent(obj);
				}
				index_ins++;
				long timeEnd = System.currentTimeMillis();
				if (timeEnd - timeStart > 25000) {
					break;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			resp.setHeader("error", URLEncoder.encode(e.getLocalizedMessage(),
					charset));
		}
		resp.setIntHeader("insert", index_ins);
		return index_ins;
	}

	private int delete(Long version, PersistenceManager pm, String cmd)
			throws UnsupportedEncodingException {
		int index_del = 0;
		try {
			List result = parserCmd(cmd, pm, DELETE);
			Iterator tempItr = result.iterator();
			while (tempItr.hasNext()) {
				pm.deletePersistent(tempItr.next());
				index_del++;
			}
		} catch (Exception e) {
			e.printStackTrace();
			resp.setHeader("error", URLEncoder.encode(e.getLocalizedMessage(),
					charset));
		}
		resp.setIntHeader("delete", index_del);
		return index_del;
	}

	private byte[] object2Bytes(Object obj) throws IOException {
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		ObjectOutputStream oos = new ObjectOutputStream(bos);
		oos.writeObject(obj);
		return bos.toByteArray();
	}

	private List parserCmd(String cmd, PersistenceManager pm, int type){
		 List result = null;
		if(cmd.endsWith("<qe>")){
			cmd = cmd.replace("<qe>", "");
			if(type == DELETE){
				cmd = cmd.replace("delete ", "");
			}
			String sql = cmd.substring(0, cmd.indexOf("<import>"));
			String importSetting = StringUtils.substringBetween(cmd, "<import>", "</import>");
			String paramSetting = StringUtils.substringBetween(cmd, "<paramSetting>", "</paramSetting>");
			String orderSetting = StringUtils.substringBetween(cmd, "<orderSetting>", "</orderSetting>");
			String pamars = StringUtils.substringBetween(cmd, "<param>", "</param>");
			
			Query query = pm.newQuery(sql);
			query.declareImports(importSetting);
			query.declareParameters(paramSetting);
			if(!orderSetting.equals("null")){
				query.setOrdering(orderSetting);
			}
			
			if(type == SELECT){
				long offset = Long.parseLong(req.getHeader("offset"));
				long startIndex = Long.parseLong(req.getHeader("startIndex"));
				query.setRange(startIndex, startIndex + offset);
			}
			
			result = (List) query.executeWithArray(getPatams(importSetting, paramSetting, pamars));
			
		}else{
			if(type == DELETE){
				cmd = cmd.replace("delete", "select");
			}
			result = (List) pm.newQuery(cmd).execute();
		}
		return result;
	}

	private Object[] getPatams(String importSetting, String paramSetting,
			String pamarsTemp) {
		Map<String, String> classMap = getClassMap(importSetting);
		String[] temps = paramSetting.split(",");
		String[] params = pamarsTemp.split("\\|");
		Object[] reslut = new Object[temps.length];
		try {
			for (int i = 0; i < temps.length; i++) {
				String[] temps2 = temps[i].split(" ");
				for (String temp2 : temps2) {
					if (classMap.containsKey(temp2)) {
						String className = classMap.get(temp2);
						if (className.equals("java.util.Date")) {
							reslut[i] = sf.parse(params[i]);
						} else {
							reslut[i] = Class.forName(className).getConstructor(String.class).newInstance(params[i]);
						}
						break;
					}
				}

			}
		} catch (Exception e) {
			e.printStackTrace();
		} 
		return reslut;
	}

	private Map<String, String> getClassMap(String importSetting) {
		Map<String, String> classMap = new HashMap<String, String>();
		String[] temps = importSetting.split(";");
		for (String temp : temps) {
			String className = StringUtils.substringAfter(temp, "import ")
					.trim();
			String key = className.substring(className.lastIndexOf(".") + 1);
			classMap.put(key, className);
		}
		return classMap;
	}

	public static void main(String[] args) {
		Uploader loader = new Uploader(null);
		String cmd = "SELECT FROM test.jdo.QueryKeyword WHERE date >= startDate && date <= endDate && id > readId<import>import java.util.Date;import java.lang.Long;</import><paramSetting>Date endDate,Date startDate,Long readId</paramSetting><orderSetting>id asc</orderSetting><param>2009-11-10|2009-11-11|40|</param><qe>";
		loader.parserCmd(cmd, null, SELECT);
	}
}
